﻿using System;
using System.Collections.Generic;
using System.Linq;
using Shell.KnowledgeBase;

namespace Shell.Nodes
{
    public class OnthologyMatchNode : BranchNode
    {
        private readonly Statement[] _statements;
        private readonly BindingContext _bindingContext;
        private readonly IEnumerable<Statement> _facts;
        private readonly IEnumerable<Implication> _implications;
        private readonly Func<BindingContext, Node> _next;

        public OnthologyMatchNode(
            Statement[] statements,
            BindingContext bindingContext,
            IEnumerable<Statement> facts,
            IEnumerable<Implication> implications,
            Func<BindingContext, Node> next)
        {
            _statements = statements;
            _bindingContext = bindingContext;
            _facts = facts;
            _implications = implications;
            _next = next;
        }


        public override IEnumerable<WeightedNode> Process()
        {
            if (_statements.Length > 10)
            {
                return Enumerable.Empty<WeightedNode>();
            }


            var resultDirect = ProveSubgoalDirect().ToArray();
            var resultImplication = ProveSubgoalImplication().ToArray();


            return Identity(resultDirect.Concat(resultImplication));
        }

        public override string ToString()
        {
            return string.Format(
                "Match onthology: {0}",
                string.Join("\n", _statements.Select(_ => _.ToString())));
        }

        private IEnumerable<OnthologyMatchNode> ProveSubgoalImplication()
        {
            var context = _bindingContext;
            var first = _statements.First();
            var rest = _statements.Skip(1);

            var boundImplications = _implications
                                                  .SelectMany(
                                                      implication =>
                                                      UnificationModule.UnifyStatementsEx(first, implication.Consequent, context)
                                                                       .Select(_ => new { BindingContext = _, Implication = implication }))
                                                  .ToArray();

            var result = boundImplications.Select(
                _ => new OnthologyMatchNode(
                         _.Implication.Antecedents.Concat(rest).Select(d => d.Resolve(_.BindingContext)).ToArray(),
                         _.BindingContext,
                         _facts,
                         _implications,
                         _next));

            return result;
        }

        private IEnumerable<Node> ProveSubgoalDirect()
        {
            var context = _bindingContext;
            var first = _statements.First();
            var rest = _statements.Skip(1);

            var directResults = ProveStatementDirect(_facts, first, context).ToArray();

            if (rest.Any())
            {
                var newNodes =
                    directResults.Select(
                        _ => new OnthologyMatchNode(
                                 rest.Select(d => d.Resolve(_)).ToArray(),
                                 _,
                                 _facts,
                                 _implications,
                                 _next));

                return newNodes;
            }
            else
            {
                return directResults.Select(_ => _next(_));
            }
        }

        private static IEnumerable<BindingContext> ProveStatementDirect(IEnumerable<Statement> statements, Statement statement, BindingContext bindingContext)
        {
            return statements
                .SelectMany(_ => UnificationModule.UnifyStatementsEx(statement, _, bindingContext))
                .ToArray();
        }
    }
}